package GameOfLife;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import FileReading.LIFFileReader;

/**
 * The Class containing the list of living cells.
 * 
 * @see Cell
 */

public class Game {
	
	/**
	 * The list of the livingCells at a given moment
	 */
	protected ArrayList<Cell> livingCells;
	/**
	 * The Array containing the different values where a living cell can survive
	 */
	public int[] valuesOfLife = { 2, 3 }; // tableau des valeurs de survie de
	/**
	 * The Array containing the different values where a dead cell can live
	 */										// base
	public int[] valuesOfDeath = { 3 }; // tableau des valeurs de mort de base
	
	/**
	 * The constructor of Game Class
	 * 
	 * @param fileName
	 * 			A String that contains the path of the .LIF
	 */
	public Game(String fileName) {
		this.livingCells = new LIFFileReader(fileName).listOfCells;
		Collections.sort(this.livingCells);
	}
	
	public Game(ArrayList<Cell> list){
		livingCells = list;
	}
	
	
	/*
	public Game(ArrayList<Cell> l) {
		this.livingCells = l;
	}
	
	public void simulation(int nbTours) {
		for(int i = 0 ; i < nbTours ; i++) {
			this.livingCells = this.nextTime();
			System.out.println(this.livingCells);
			System.out.println("--------------------------");
		}
	}*/
	
	/**
	 * For each Cell c in the ArrayList returned by neighbours() : counts the number of c's living neighbours and then tests if 
	 * c can live or survive depending on c's state.   
	 * 
	 
	 * 
	 * @return nextTime
	 * 			An ArrayList that contains new living cells
	 * 
	 * @see neighbours()
	 */
	public ArrayList<Cell> nextTime() {
		ArrayList<Cell> nextTime = new ArrayList<Cell>();
		ArrayList<Cell> l = this.neighbours();
		
		Iterator<Cell> itr = l.iterator();
		while(itr.hasNext()) {
			Cell c = itr.next();

			int neighboursAlive = 0;
			
			neighboursAlive = this.neighboursAlive(c);
			
			boolean willLive = false;
			
			int i=0;
			if(c.isAlive()) {
				while(i < this.valuesOfLife.length && !willLive  ) {
					if(neighboursAlive == this.valuesOfLife[i]) {
						willLive = true;
						this.addCell(nextTime, new Cell(true, c.getPosition()));
					}
					i++;
				}
			} else {
				while(i < this.valuesOfDeath.length && !willLive  ) {
					if(neighboursAlive == this.valuesOfDeath[i]) {
						willLive = true;
						this.addCell(nextTime, new Cell(true, c.getPosition()));
					}
					i++;
				}
			}
			
		}
		
		l.clear();
		l.addAll(this.livingCells);
		this.livingCells = nextTime;
		Collections.sort(this.livingCells);
		return l;
	}
	
	/**
	 * Creates a copy that contains the living Cells and its neighbours, then return it.
	 */
	protected ArrayList<Cell> neighbours() {
		ArrayList<Cell> cells = new ArrayList<Cell>() ;
		cells.addAll(this.livingCells);
		
		Iterator<Cell> itr = this.livingCells.iterator();
		while(itr.hasNext()) {
			Cell c = itr.next();
			int x = c.getPosition().getX();
			int y = c.getPosition().getY();
			
			for(int i = -1; i<2 ; i++) {
				for(int j = -1; j<2 ; j++) {
					Cell cell = new Cell(false,new Position(x+i, y+j));
					this.addCell(cells, cell);
				}
			}
		}
		Collections.sort(cells);
		return cells;
	}

	/**
	 * Calls findCell on each neighbour of c and return the number of c's living cells.
	 * 
	 * @param c, a Cell
	 * @return The number of c's living neighbours 
	 */
	protected int neighboursAlive(Cell c) {
		int numberOfNeighboursAlive = 0;
		for(int i = -1; i<2 ; i++) {
			for(int j = -1; j<2 ; j++) {
				if(!(i == 0 && j == 0) && this.findCell(this.livingCells,new Cell(true,new Position(c.getPosition().getX() + i, c.getPosition().getY() + j)))) {
					numberOfNeighboursAlive++;
				}
			}
		}
		return numberOfNeighboursAlive;
	}
	
	/**
	 * Affiche un plateau de 50x50 en partant de -25
	 */
	public void print() {
		for(int i = -5; i<5 ; i++) {
			for(int j = -5; j<5 ; j++) {
				if(findCell(this.livingCells, new Cell(true, new Position(j,i)))) {
					System.out.print("o");
				} else {
					System.out.print(" ");
				}
			}
			System.out.println();
		}
	}
	
	/**
	 * Recherche si la cellule c est dans l avec une recherche dichotomique.
	 * 
	 * @param l
	 * @param c
	 * @return
	 */
	protected boolean findCell(ArrayList<Cell> l, Cell c) {
		Cell temp;
		
		int start = 0;
		int end = l.size()-1;
		int center;
		while(start <= end) {
			center = (start + end) /2;
			temp = l.get(center);
			switch(temp.compareTo(c)) {
			
			case -1 :
				start = center + 1;
				break;
			case 0 :
				return true;
			case 1 :
				end = center - 1;
				break;
			}
		}
		return false;
	}
	
	/**
	 * Adds the Cell c to a List l at the right index if l does not contain it already. 
	 * 
	 * @param l
	 * 			The ArrayList where will be added c
	 * @param c
	 * 			The cell that may be added
	 */
	protected void addCell(ArrayList<Cell> l, Cell c) {
		if(!l.contains(c)) {
			int index = -1;
			
			int x = c.getPosition().getX();
			int y = c.getPosition().getY();
			
			Iterator<Cell> itr = l.iterator();
			while((index == -1) && itr.hasNext()) {
				Cell i = itr.next();
				if((i.getPosition().getY() == y && i.getPosition().getX() > x) || i.getPosition().getY() > y) {
					index = l.indexOf(i);
				}
			}
			if(index == -1) {
				l.add(c);
			}
			else {
				l.add(index, c);
			}
		}
	}
	


	public ArrayList<Cell> getLivingCells() {
		return livingCells;
	}

	public void setLivingCells(ArrayList<Cell> livingCells) {
		this.livingCells = livingCells;
	}


	/**
	 * Launches the program for a certain duration of time
	 * 
	 * @param time
	 *            : the time in seconds
	 */
	public void run(long time){
		
		if(time<0) {
			return;
		}
		
		long sv = System.currentTimeMillis();
		
		while (true) {
			
			this.livingCells = this.nextTime();
			
			System.out.println(this.livingCells);
			
			if (sv+time < System.currentTimeMillis()) {
				break;
			}
			
		}
		
		System.out.println(System.currentTimeMillis()-sv);
		
	}

	
	/*
	 * Une fonction(ArrayList<Cell> l ,int x, int y) et qui d�cale chaque cell de l de x et y.
	 * 
	 * Une fonction equals pour game comparant leurs list living cells.
	 * 
	 * Une fonction qui copie(ArrayList<Cell> l) qui copie cette liste et chaque �l�ment aussi.
	 */
	
	public boolean estUneTranslation(ArrayList<Cell> list, Position p) {
		
		int x=0;
		int y=0;
		
		if(list.size()== this.getLivingCells().size()){
			Cell c1 = list.get(0);
			Cell c2 = this.getLivingCells().get(0);
			
			
			x= c2.getPosition().getX()-c1.getPosition().getX();
			y= c2.getPosition().getY()-c1.getPosition().getY();

			for(int i=0; i<list.size();i++) {
				Cell c =list.get(i);
				Cell c3 = this.getLivingCells().get(i);
				if(c.getPosition().getX()+x!= c3.getPosition().getX() || c.getPosition().getY()+y != c3.getPosition().getY()){
				 return false;	
				}
			}
			return true;
		}		
		return false;

	}
	
	
}
